二叉搜索树虽然可以缩短查找的效率,但如果数据有序或者接近有序,二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率非常低下
AVL树的概念
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
1.它的左右子树都是AVL树;
2.左子树和右子树高度之差(简称平衡因子)的绝对值不超过1(故一个结点的平衡因子可以是-1/0/1)
如果一棵二叉搜索树是高度平衡的,它就是AVL树,如果它有n个结点,其高度可保持在0(lgn),平衡搜索时间复杂度为O(lgn)
平衡化旋转
如果在一棵原本是平衡的二叉搜索树中新插入一个结点,可能造成二叉搜索树的不平衡,此时必须调整树的结构,使之平衡化
左单旋
左单旋:插入点的位置是较高右子树的右侧
左单旋需要考虑的情况
结点90为空的情况:
新插入结点位于120的右边
新插入结点位于120的左边
右单旋
插入结点的位置是较高左子树的左侧
第一种情况:
第二种情况:
第三种情况
先左后右旋转
我们只经过一次调整,是达不到目的的,所以混合使用左单旋和右单旋
插入点位置是较高左子树的右侧
第一种情况:
第二种情况:
第三种情况:
先右后左旋转
插入结点的位置是较高右子树的左侧
第一种情况:
第二种情况:
第三种情况:
AVL的代码实现
实现对以下序列的插入
代码
#include<iostream>
using namespace std;
#define NULL 0
template<class K,class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _pLeft;
AVLTreeNode<K, V>* _pRight;
AVLTreeNode<K, V>*_pParent;
K _key;//需要插入结点的值
V _value;//代表的是某插入节点的下标
int _bf;//平衡因子
AVLTreeNode(const K& key, const V& value)
:_pLeft(NULL)
, _pRight(NULL)
, _pParent(NULL)
, _key(key)
, _value(value)
, _bf(0)
{}
};
template<class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
typedef AVLTreeNode<K, V>* PNode;
public:
AVLTree()
:_pRoot(NULL)
{}
bool Insert(const K& key, const V& value)
{
//树为空
if (NULL == _pRoot)
{
_pRoot = new Node(key, value);
return true;
}
//树不为空
//寻找结点应该插入的位置
PNode pCur = _pRoot;
PNode pParent = NULL;
while (pCur)
{
if (key < pCur->_key)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else if (key>pCur->_key)
{
pParent = pCur;
pCur = pCur->_pRight;
}
else
return false;
}
//插入新结点
pCur = new Node(key, value);
if (key < pParent->_key)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
//与搜索二叉树不同的是,此处有双亲结点,故一定要记得更新双亲结点
pCur->_pParent = pParent;
//更新平衡因子
while (pParent)
{
if (pCur == pParent->_pLeft)
pParent->_bf--;
else
pParent->_bf++;
if (0 == pParent->_bf)//双亲结点的平衡因子为0
return true;
else if (1 == pParent->_bf || -1 == pParent->_bf)
{
//接着朝上调整
pCur = pParent;
pParent = pCur->_pParent;
}
else
{
//2(进行旋转)
if (2 == pParent->_bf)
{
if (1 == pCur->_bf)
_RotateL(pParent);
else
_RotateRL(pParent);
}
else
{
if (-1 == pCur->_bf)
_RotateR(pParent);
else
_RotateLR(pParent);
}
break;
}
}
return true;
}
//求取树的高度
int Depth()
{
return _Depth(_pRoot);
}
//中序遍历输出树的结点
void InOrder()
{
_InOrder(_pRoot);
}
//判断AVL树是否平衡
bool IsBalance()
{
int depth;
return _IsBalance(_pRoot, depth);
}
private:
//求取树的高度
int _Depth(PNode pRoot)
{
if (NULL == pRoot)
return 0;
int leftDepth = 0;
int rightDepth = 0;
leftDepth =_Depth(pRoot->_pLeft);
rightDepth = _Depth(pRoot->_pRight);
//给加1是因为在递归求高度时,最后没有求根结点,故必须在额外加上1
return leftDepth > rightDepth ? (leftDepth + 1):(rightDepth+1)
}
//判断AVL树是否平衡
bool _IsBalance(PNode pRoot, int &depth)
{
if (NULL == pRoot)
{
depth = 0;
return NULL;
}
int leftDepth = 0;
int rightDepth = 0;
//如果根结点的左子树不是AVL树,那就直接返回,不用再做别的判断
if (false == _IsBalance(pRoot->_pLeft, leftDepth))
return false;
if (false == _IsBalance(pRoot->_pRight, rightDepth))
return false;
//如果右子树的高度与左子树的高度之差不等于根结点的平衡因子,那说明此树不是AVL树
if (rightDepth - leftDepth != pRoot->_bf)
{
cout << "<" << pRoot->_key << pRoot->_value << ">" << "bf异常--->" << pRoot->_bf << endl;
return false;
}
//保存着高度,备用
depth = leftDepth > rightDepth ? (leftDepth + 1) : (rightDepth + 1);
return true;
}
void _InOrder(PNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << "<"<<pRoot->_key <<","<< pRoot->_value << ">"<<" ";
cout << endl;
_InOrder(pRoot->_pRight);
}
}
void _RotateL(PNode pParent)//左单旋
{
PNode pSubR = pParent->_pRight;
PNode pSubRL = pSubR->_pLeft;
pParent->_pRight = pSubRL;
if (pSubRL)//只有此结点非空,才可以更新此结点的双亲结点
pSubRL->_pParent = pParent;
pSubR->_pLeft = pParent;
PNode pPParent = pParent->_pParent;//,在更新pParent的双亲结点之前,保存pParent的双亲结点
pParent->_pParent = pSubR;//先保存Ppparent,在更新pParent的父亲结点
pSubR->_pParent = pPParent;
if (NULL == pPParent)//pParent是根结点
_pRoot = pSubR;
else//pParnet不是根结点
{
if (pParent == pPParent->_pLeft)
pPParent->_pLeft = pSubR ;//pSubR现在替代了pParent的位置
else
pPParent->_pRight = pSubR;
//旋转过程中,发现只有pParent和pSubR的平衡因子变化了
pParent->_bf = pSubR->_bf = 0;
}
}
void _RotateR(PNode pParent)
{
PNode pSubL = pParent->_pLeft;
PNode pSubLR = pSubL->_pRight;
pParent->_pLeft = pSubLR;
if (pSubLR)
pSubLR->_pParent = pParent;
pSubL->_pRight= pParent;
PNode pPParent = pParent->_pParent;
pParent->_pParent = pSubL;//先保存Ppparent,在更新pParent的父亲结点
pSubL->_pParent = pPParent;//无论pParent是否是根结点,先把pSubL的双亲先更新,下面就不用考虑更新pSubL的双亲结点了
if (NULL == pPParent)
_pRoot = pSubL;
else
{
if (pParent == pPParent->_pLeft)
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}
//旋转过程中,发现只有pParnet和pSubL的平衡因子变化了
pParent->_bf = pSubL->_bf = 0;
}
void _RotateLR(PNode pParent)
{
PNode pSubL = pParent->_pLeft;
PNode pSubLR = pSubL->_pRight;
int bf = pSubLR->_bf;
//先以pParent的左子树为双亲结点,把此树看成是一棵树,进行左旋转
_RotateL(pParent->_pLeft);
//在把整棵树进行右旋转
_RotateR(pParent);
if (1 == pSubLR->_bf)//插入结点后,此时的pSubLR的平衡因子为1
pSubL->_bf = -1;
else if (-1 == pSubLR->_bf)
pParent->_bf = 1;
}
void _RotateRL(PNode pParent)
{
PNode pSubR = pParent->_pRight;
PNode pSubRL = pSubR->_pLeft;
int bf = pSubRL->_bf;
//先以pParent的右子树为双亲结点,把此树看成是一棵树,进行右旋转
_RotateR(pParent->_pRight);
//再把整棵树进行左旋转
_RotateL(pParent);
if (1 == pSubRL->_bf)
pParent->_bf = -1;
else if(-1 == pSubRL->_bf)
pSubR->_bf = 1;
}
private:
PNode _pRoot;
};
void TestAVLTree()
{
//1.普通场景
int array1[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
AVLTree<int, int> tree1;
//插入结点
for (size_t i = 0; i < sizeof(array1) / sizeof(array1[0]); ++i)
tree1.Insert(array1[i], i);//key表示的是data,而value表示的是此data对应的下标
cout << "普通场景下的二叉树:" << endl;
cout << endl;
//中序遍历输出结点,检查结点是否插入正确
tree1.InOrder();
cout << endl;
//检查树是否平衡
if (tree1.IsBalance())
cout << "普通场景:tree1是平衡的" << endl;
else
cout << "普通场景:tree1是平衡的" << endl;
cout << endl;
//1.特殊场景
int array2[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
AVLTree<int, int> tree2;
//插入结点
for (size_t i = 0; i < sizeof(array2) / sizeof(array2[0]); ++i)
tree2.Insert(array2[i], i);//key表示的是data,而value表示的是此data对应的下标
cout << "特殊场景下的二叉树:" << endl;
//中序遍历输出结点,检查结点是否插入正确
tree2.InOrder();
cout << endl;
//检查树是否平衡
if (tree2.IsBalance())
cout << "特殊场景:tree2是平衡的" << endl;
else
cout << "特殊场景:tree2是平衡的" << endl;
}
int main()
{
TestAVLTree();
system("pause");
return 0;
}
运行结果: